//
// Created by martin on 5/1/22.
//

#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <libgen.h>
#include <assert.h>
#include <stdlib.h>

/**
 * Run command:
 * $ ./a.out 127.0.0.1 2009
 */
int main(int argc, char* argv[])
{
    if (argc < 3) {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);
    addr.sin_port = htons(static_cast<uint16_t>(port));

    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(sockfd >= 0);
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); // 端口复用

    int ret = bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
    assert(ret != -1);
    ret = listen(sockfd, 5);
    assert(ret != -1);

    struct sockaddr_in client;
    socklen_t client_addrlen = sizeof(client);
    int connfd = accept(sockfd, reinterpret_cast<sockaddr*>(&client), &client_addrlen);
    if (connfd < 0) {
        perror("accept error");
    }
    else {
        int pipefd[2];
        ret = pipe(pipefd); /* 创建管道 */
        assert(ret != -1);

        while (1) {
            ssize_t res;
            /* 将connfd上流入数据定向到管道中 */
            res = splice(connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
            if (res == 0) { // 收到EOF(FIN分节), 则断开连接
                break;
            }
            assert(res != -1);

            /* 将管道输出定向到connfd客户连接的文件描述符 */
            res = splice(pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
            assert(res != -1);
        }
        close(connfd);
    }

    close(sockfd);
    return 0;
}
